/*****************************************************************
setpwc-astro, fork from setpwc
(c) 2003-2006 by folkert@vanheusden.com
(c) 2014 by Thx8411

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License v2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA  02110-1301, USA.
******************************************************************/

#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define _LINUX_TIME_H 1	/* to get things compile on kernel 2.6.x */
#include <linux/videodev2.h>

#include "pwc-ioctl.h"

char *device = "/dev/video0";

#define SET_PAN		0
#define SET_TILT	1

void error_exit(char *what_ioctl)
{
	fprintf(stderr, "Error while doing ioctl %s: %s\n", what_ioctl, strerror(errno));

	/* commented out: some versions of the driver seem to return
	 * unexpected errors */
	/* exit(1); */
}

void check_device(int *fd)
{
	if (*fd == -1)
	{
		/* open device */
		*fd = open(device, O_RDWR);
		if (*fd == -1)
		{
			fprintf(stderr, "Error while accessing device %s: %s\n", device, strerror(errno));
			exit(1);
		}
	}
}

void not_supported(char *what)
{
	printf("%s is not supported by the combination\n", what);
	printf("of your webcam and the driver.\n");
}

void dump_current_settings_ctrl(int fd, const char *prefix, int id)
{
	struct v4l2_control vctrl;
	struct v4l2_queryctrl qctrl;
	struct v4l2_querymenu qmenu;
	const char *flags[10];
	int flags_cnt = 0;
	memset(&vctrl, 0, sizeof(vctrl));
	memset(&qctrl, 0, sizeof(qctrl));
	vctrl.id = id;
	qctrl.id = id;
	if (ioctl(fd, VIDIOC_G_CTRL, &vctrl) == -1) {
		printf("%s: VIDIOC_G_CTRL failed: %s\n", prefix, strerror(errno));
		return;
	}
	if (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == -1) {
		printf("%s: VIDIOC_QUERYCTRL failed: %s\n", prefix, strerror(errno));
		return;
	}
	if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED)
		flags[flags_cnt++] = "disabled";
	if (qctrl.flags & V4L2_CTRL_FLAG_GRABBED)
		flags[flags_cnt++] = "grabbed";
	if (qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY)
		flags[flags_cnt++] = "read-only";
	if (qctrl.flags & V4L2_CTRL_FLAG_UPDATE)
		flags[flags_cnt++] = "update";
	if (qctrl.flags & V4L2_CTRL_FLAG_INACTIVE)
		flags[flags_cnt++] = "inactive";
	if (qctrl.flags & V4L2_CTRL_FLAG_SLIDER)
		flags[flags_cnt++] = "slider";
	flags[flags_cnt] = NULL;
	switch (qctrl.type) {
	case V4L2_CTRL_TYPE_INTEGER:
		printf("%s: %s\n", prefix, qctrl.name);
		printf("  Value:   %d\n", vctrl.value);
		printf("  Minimum: %d\n", qctrl.minimum);
		printf("  Maximum: %d\n", qctrl.maximum);
		printf("  Step:    %d\n", qctrl.step);
		printf("  Default: %d\n", qctrl.default_value);
		printf("  Flags:   ");
		for (flags_cnt = 0; flags[flags_cnt]; flags_cnt++)
			printf("%s%s", flags_cnt ? "," : "", flags[flags_cnt]);
		printf("\n");
		break;
	case V4L2_CTRL_TYPE_BOOLEAN:
		printf("%s: %s\n", prefix, qctrl.name);
		printf("  Value:   %s\n", vctrl.value ? "enabled" : "disabled");
		printf("  Default: %s\n", qctrl.default_value ? "enabled" : "disabled");
		printf("  Flags:   ");
		for (flags_cnt = 0; flags[flags_cnt]; flags_cnt++)
			printf("%s%s", flags_cnt ? "," : "", flags[flags_cnt]);
		printf("\n");
		break;
	case V4L2_CTRL_TYPE_MENU:
		printf("%s: %s\n", prefix, qctrl.name);
		memset(&qmenu, 0, sizeof(qmenu));
		qmenu.id    = id;
		qmenu.index = vctrl.value;
		if (ioctl(fd, VIDIOC_QUERYMENU, &qmenu) == -1)
			printf("  Value:   ??? (%d)\n", vctrl.value);
		else
			printf("  Value:   %s (%d)\n", qmenu.name, vctrl.value);
		memset(&qmenu, 0, sizeof(qmenu));
		qmenu.id    = id;
		qmenu.index = qctrl.default_value;
		if (ioctl(fd, VIDIOC_QUERYMENU, &qmenu) == -1)
			printf("  Default: ??? (%d)\n", qctrl.default_value);
		else
			printf("  Default: %s (%d)\n", qmenu.name, qctrl.default_value);
		printf("  Flags:   ");
		for (flags_cnt = 0; flags[flags_cnt]; flags_cnt++)
			printf("%s%s", flags_cnt ? "," : "", flags[flags_cnt]);
		printf("\n");
		break;
	case V4L2_CTRL_TYPE_BUTTON:
		printf("%s: %s\n", prefix, qctrl.name);
		printf("  Unsupport type: button\n");
		printf("  Flags:   ");
		for (flags_cnt = 0; flags[flags_cnt]; flags_cnt++)
			printf("%s%s", flags_cnt ? "," : "", flags[flags_cnt]);
		printf("\n");
		break;
	case V4L2_CTRL_TYPE_INTEGER64:
		printf("%s: %s\n", prefix, qctrl.name);
		printf("  Unsupport type: integer64\n");
		printf("  Flags:   ");
		for (flags_cnt = 0; flags[flags_cnt]; flags_cnt++)
			printf("%s%s", flags_cnt ? "," : "", flags[flags_cnt]);
		printf("\n");
		break;
	case V4L2_CTRL_TYPE_CTRL_CLASS:
		printf("%s: %s\n", prefix, qctrl.name);
		printf("  Unsupport type: class\n");
		printf("  Flags:   ");
		for (flags_cnt = 0; flags[flags_cnt]; flags_cnt++)
			printf("%s%s", flags_cnt ? "," : "", flags[flags_cnt]);
		printf("\n");
		break;
	case V4L2_CTRL_TYPE_STRING:
		printf("%s: %s\n", prefix, qctrl.name);
		printf("  Unsupport type: string\n");
		printf("  Flags:   ");
		for (flags_cnt = 0; flags[flags_cnt]; flags_cnt++)
			printf("%s%s", flags_cnt ? "," : "", flags[flags_cnt]);
		printf("\n");
		break;
	}
}

void dump_current_settings(int fd)
{
	struct v4l2_capability vcap;
	struct v4l2_format pixfmt;
	struct pwc_probe pwcp;
	int dummy;
	struct pwc_whitebalance pwcwb;
	struct pwc_leds pwcl;
	struct pwc_mpt_range pmr;
	struct pwc_mpt_angles pma;
	struct pwc_serial ps;

	/* get name */
	if (ioctl(fd, VIDIOC_QUERYCAP, &vcap) == -1)
		error_exit("VIDIOC_QUERYCAP");
	printf("Current device: %s (%s @ %s)\n", (const char *)vcap.card, (const char *)vcap.driver, (const char *)vcap.bus_info);

	/* verify that it IS a Philips Webcam */
	if (ioctl(fd, VIDIOCPWCPROBE, &pwcp) == -1)
		error_exit("VIDIOCPWCPROBE");
	if (strcmp((const char *)vcap.card, pwcp.name) != 0)
		printf("Warning: this might not be a Philips compatible webcam!\n");
	printf("VIDIOCPWCPROBE returns: %s - %d\n", pwcp.name, pwcp.type);

	if (ioctl(fd, VIDIOCPWCGSERIAL, &ps) == -1)
		error_exit("VIDIOCPWCGSERIAL");
	printf("Serial number: %s\n", ps.serial);

	/* get resolution/framerate */
	memset(&pixfmt, 0, sizeof(pixfmt));
	pixfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	if (ioctl(fd, VIDIOC_G_FMT, &pixfmt) == -1)
		error_exit("VIDIOC_G_FMT");
	printf("Resolution (x, y): %d, %d\n", pixfmt.fmt.pix.width, pixfmt.fmt.pix.height);
	if (pixfmt.fmt.pix.priv & PWC_FPS_FRMASK)
		printf("Framerate: %d\n", (pixfmt.fmt.pix.priv & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT);

	/* color (etc.) settings */
	dump_current_settings_ctrl(fd, "Brightness", V4L2_CID_BRIGHTNESS);
	dump_current_settings_ctrl(fd, "Hue", V4L2_CID_HUE);
	dump_current_settings_ctrl(fd, "Colour", V4L2_CID_SATURATION);
	dump_current_settings_ctrl(fd, "Contrast", V4L2_CID_CONTRAST);
	dump_current_settings_ctrl(fd, "Whiteness", V4L2_CID_WHITENESS);
	printf("Palette: ");
	switch(pixfmt.fmt.pix.pixelformat) {
	case V4L2_PIX_FMT_GREY:
		printf("Linear intensity grey scale (255 is brightest).\n");
		break;
	case V4L2_PIX_FMT_HI240:
		printf("The BT848 8bit colour cube.\n");
		break;
	case V4L2_PIX_FMT_RGB565:
		printf("RGB565 packed into 16 bit words.\n");
		break;
	case V4L2_PIX_FMT_RGB555:
		printf("RGV555 packed into 16 bit words, top bit undefined.\n");
		break;
	case V4L2_PIX_FMT_BGR24:
		printf("RGB888 packed into 24bit words.\n");
		break;
	case V4L2_PIX_FMT_BGR32:
		printf("RGB888 packed into the low 3 bytes of 32bit words. The top 8bits are undefined.\n");
		break;
	case V4L2_PIX_FMT_YUYV:
		printf("Video style YUV422 - 8bits packed 4bits Y 2bits U 2bits V\n");
		break;
	case V4L2_PIX_FMT_UYVY:
		printf("Describe me\n");
		break;
	case V4L2_PIX_FMT_Y41P:
		printf("YUV411 capture\n");
		break;
	case V4L2_PIX_FMT_YUV422P:
		printf("YUV 4:2:2 Planar\n");
		break;
	case V4L2_PIX_FMT_YUV411P:
		printf("YUV 4:1:1 Planar\n");
		break;
	case V4L2_PIX_FMT_YVU420:
		printf("YUV 4:2:0 Planar\n");
		break;
	case V4L2_PIX_FMT_YVU410:
		printf("YUV 4:1:0 Planar\n");
		break;
	default:
		printf("Unknown! (%d - %c%c%c%c)\n", pixfmt.fmt.pix.pixelformat, pixfmt.fmt.pix.pixelformat & 0xff, (pixfmt.fmt.pix.pixelformat >> 8) & 0xff, (pixfmt.fmt.pix.pixelformat >> 16) & 0xff, (pixfmt.fmt.pix.pixelformat >> 24) & 0xff);
	}

	if (ioctl(fd, VIDIOCPWCGCQUAL, &dummy) == -1)
		error_exit("VIDIOCPWCGCQUAL");
	printf("Compression preference: %d\n", dummy);

	if (ioctl(fd, VIDIOCPWCGAGC, &dummy) == -1)
		error_exit("VIDIOCPWCGAGC");
	printf("Automatic gain control: %d\n", dummy);

	if (ioctl(fd, VIDIOCPWCGAWB, &pwcwb) == -1)
		error_exit("VIDIOCPWCGAWB");
	printf("Whitebalance mode: ");
	if (pwcwb.mode == PWC_WB_AUTO)
		printf("auto\n");
	else if (pwcwb.mode == PWC_WB_MANUAL)
		printf("manual (red: %d, blue: %d)\n", pwcwb.manual_red, pwcwb.manual_blue);
	else if (pwcwb.mode == PWC_WB_INDOOR)
		printf("indoor\n");
	else if (pwcwb.mode == PWC_WB_OUTDOOR)
		printf("outdoor\n");
	else if (pwcwb.mode == PWC_WB_FL)
		printf("artificial lightning ('fl')\n");
	else
		printf("unknown!\n");

	if (ioctl(fd, VIDIOCPWCGLED, &pwcl) != -1)
	{
		printf("Led ON time: %d\n", pwcl.led_on);
		printf("Led OFF time: %d\n", pwcl.led_off);
	}
	else
	{
		not_supported("Blinking of LED");
	}

	if (ioctl(fd, VIDIOCPWCGCONTOUR, &dummy) == -1)
		error_exit("VIDIOCPWCGCONTOUR");
	printf("Sharpness: %d\n", dummy);

	if (ioctl(fd, VIDIOCPWCGBACKLIGHT, &dummy) == -1)
		error_exit("VIDIOCPWCGBACKLIGHT");
	printf("Backlight compensation mode: ");
	if (dummy == 0) printf("off\n"); else printf("on\n");

	if (ioctl(fd, VIDIOCPWCGFLICKER, &dummy) != -1)
	{
		printf("Anti-flicker mode: ");
		if (dummy == 0) printf("off\n"); else printf("on\n");
	}
	else
	{
		not_supported("Anti-flicker mode");
	}

	if (ioctl(fd, VIDIOCPWCGDYNNOISE, &dummy) != -1)
	{
		printf("Noise reduction mode: %d ", dummy);
		if (dummy == 0) printf("(none)");
		else if (dummy == 3) printf("(high)");
		printf("\n");
	}
	else
	{
		not_supported("Noise reduction mode");
	}

	if (ioctl(fd, VIDIOCPWCMPTGRANGE, &pmr) == -1)
	{
		not_supported("Pan/tilt range");
	}
	else
	{
		printf("Pan min. : %d, max.: %d\n", pmr.pan_min, pmr.pan_max);
		printf("Tilt min.: %d, max.: %d\n", pmr.tilt_min, pmr.tilt_max);
	}

	pma.absolute=1;
	if (ioctl(fd, VIDIOCPWCMPTGANGLE, &pma) == -1)
	{
		not_supported("Get pan/tilt position");
	}
	else
	{
		printf("Pan  (degrees * 100): %d\n", pma.pan);
		printf("Tilt (degrees * 100): %d\n", pma.tilt);
	}
}

void query_pan_tilt_status(fd)
{
	struct pwc_mpt_status pms;

	if (ioctl(fd, VIDIOCPWCMPTSTATUS, &pms) == -1)
		error_exit("VIDIOCPWCMPTSTATUS");

	printf("Status: %d\n", pms.status);
	printf("Time pan: %d\n", pms.time_pan);
	printf("Time tilt: %d\n", pms.time_tilt);
}

void reset_pan_tilt(int fd, int what)
{
	if (ioctl(fd, VIDIOCPWCMPTRESET, &what) == -1)
		error_exit("VIDIOCPWCMPTRESET");
}

void set_pan_or_tilt(int fd, char what, int value)
{
	struct pwc_mpt_angles pma;

	pma.absolute=1;
	if (ioctl(fd, VIDIOCPWCMPTGANGLE, &pma) == -1)
		error_exit("VIDIOCPWCMPTGANGLE");

	if (what == SET_PAN)
		pma.pan = value;
	else if (what == SET_TILT)
		pma.tilt = value;

	if (ioctl(fd, VIDIOCPWCMPTSANGLE, &pma) == -1)
		error_exit("VIDIOCPWCMPTSANGLE");
}


void set_dimensions_and_framerate(int fd, int w, int h, int framerate)
{
	struct v4l2_format pixfmt;

	/* get resolution/framerate */
	if (ioctl(fd, VIDIOC_G_FMT, &pixfmt) == -1)
		error_exit("VIDIOC_G_FMT");

	if (w > 0 && h > 0)
	{
		pixfmt.fmt.pix.width = w;
		pixfmt.fmt.pix.height = h;
	}

	if (pixfmt.fmt.pix.priv & PWC_FPS_FRMASK)
	{
		/* set new framerate */
		pixfmt.fmt.pix.priv &= ~PWC_FPS_FRMASK;
		pixfmt.fmt.pix.priv |= (framerate << PWC_FPS_SHIFT);
   
		if (ioctl(fd, VIDIOC_S_FMT, &pixfmt) == -1)
			error_exit("VIDIOC_S_FMT");
	}
	else
	{
		fprintf(stderr, "This device doesn't support setting the framerate.\n");
		exit(1);
	}
}

void flash_settings(int fd)
{
	if (ioctl(fd, VIDIOCPWCSUSER) == -1)
		error_exit("VIDIOCPWCSUSER");
}

void restore_settings(int fd)
{
	if (ioctl(fd, VIDIOCPWCRUSER) == -1)
		error_exit("VIDIOCPWCRUSER");
}

void restore_factory_settings(int fd)
{
	if (ioctl(fd, VIDIOCPWCFACTORY) == -1)
		error_exit("VIDIOCPWCFACTORY");
}

void set_compression_preference(int fd, int pref)
{
	if (ioctl(fd, VIDIOCPWCSCQUAL, &pref) == -1)
		error_exit("VIDIOCPWCSCQUAL");
}

void set_automatic_gain_control(int fd, int pref)
{
	if (ioctl(fd, VIDIOCPWCSAGC, &pref) == -1)
		error_exit("VIDIOCPWCSAGC");
}

void set_shutter_speed(int fd, int pref)
{
	if (ioctl(fd, VIDIOCPWCSSHUTTER, &pref) == -1)
		error_exit("VIDIOCPWCSSHUTTER");
}

void set_automatic_white_balance_mode(int fd, char *mode)
{
	struct pwc_whitebalance pwcwb;

	if (ioctl(fd, VIDIOCPWCGAWB, &pwcwb) == -1)
		error_exit("VIDIOCPWCGAWB");

	if (strcasecmp(mode, "auto") == 0)
		pwcwb.mode = PWC_WB_AUTO;
	else if (strcasecmp(mode, "manual") == 0)
		pwcwb.mode = PWC_WB_MANUAL;
	else if (strcasecmp(mode, "indoor") == 0)
		pwcwb.mode = PWC_WB_INDOOR;
	else if (strcasecmp(mode, "outdoor") == 0)
		pwcwb.mode = PWC_WB_OUTDOOR;
	else if (strcasecmp(mode, "fl") == 0)
		pwcwb.mode = PWC_WB_FL;
	else
	{
		fprintf(stderr, "'%s' is not a known white balance mode.\n", mode);
		exit(1);
	}

	if (ioctl(fd, VIDIOCPWCSAWB, &pwcwb) == -1)
		error_exit("VIDIOCPWCSAWB");
}

void set_automatic_white_balance_mode_red(int fd, int val)
{
	struct pwc_whitebalance pwcwb;

	if (ioctl(fd, VIDIOCPWCGAWB, &pwcwb) == -1)
		error_exit("VIDIOCPWCGAWB");

	pwcwb.manual_red = val;

	if (ioctl(fd, VIDIOCPWCSAWB, &pwcwb) == -1)
		error_exit("VIDIOCPWCSAWB");
}

void set_automatic_white_balance_mode_blue(int fd, int val)
{
	struct pwc_whitebalance pwcwb;

	if (ioctl(fd, VIDIOCPWCGAWB, &pwcwb) == -1)
		error_exit("VIDIOCPWCGAWB");

	pwcwb.manual_blue = val;

	if (ioctl(fd, VIDIOCPWCSAWB, &pwcwb) == -1)
		error_exit("VIDIOCPWCSAWB");
}

void set_automatic_white_balance_speed(int fd, int val)
{
	struct pwc_wb_speed pwcwbs;

	if (ioctl(fd, VIDIOCPWCGAWBSPEED, &pwcwbs) == -1)
		error_exit("VIDIOCPWCGAWBSPEED");

	pwcwbs.control_speed = val;

	if (ioctl(fd, VIDIOCPWCSAWBSPEED, &pwcwbs) == -1)
		error_exit("VIDIOCPWCSAWBSPEED");
}

void set_automatic_white_balance_delay(int fd, int val)
{
	struct pwc_wb_speed pwcwbs;

	if (ioctl(fd, VIDIOCPWCGAWBSPEED, &pwcwbs) == -1)
		error_exit("VIDIOCPWCGAWBSPEED");

	pwcwbs.control_delay = val;

	if (ioctl(fd, VIDIOCPWCSAWBSPEED, &pwcwbs) == -1)
		error_exit("VIDIOCPWCSAWBSPEED");
}

void set_led_on_time(int fd, int val)
{
	struct pwc_leds pwcl;

	if (ioctl(fd, VIDIOCPWCGLED, &pwcl) == -1)
		error_exit("VIDIOCPWCGLED");

	pwcl.led_on = val;

	if (ioctl(fd, VIDIOCPWCSLED, &pwcl) == -1)
		error_exit("VIDIOCPWCSLED");
}

void set_led_off_time(int fd, int val)
{
	struct pwc_leds pwcl;

	if (ioctl(fd, VIDIOCPWCGLED, &pwcl) == -1)
		error_exit("VIDIOCPWCGLED");

	pwcl.led_off = val;

	if (ioctl(fd, VIDIOCPWCSLED, &pwcl) == -1)
		error_exit("VIDIOCPWCSLED");
}

void set_sharpness(int fd, int val)
{
	if (ioctl(fd, VIDIOCPWCSCONTOUR, &val) == -1)
		error_exit("VIDIOCPWCSCONTOUR");
}

void set_backlight_compensation(int fd, int val)
{
	if (ioctl(fd, VIDIOCPWCSBACKLIGHT, &val) == -1)
		error_exit("VIDIOCPWCSBACKLIGHT");
}

void set_antiflicker_mode(int fd, int val)
{
	if (ioctl(fd, VIDIOCPWCSFLICKER, &val) == -1)
		error_exit("VIDIOCPWCSFLICKER");
}

void set_noise_reduction(int fd, int val)
{
	if (ioctl(fd, VIDIOCPWCSDYNNOISE, &val) == -1)
		error_exit("VIDIOCPWCSDYNNOISE");
}

void usage(void)
{
	fprintf(stderr, "-d device  device to use (default: /dev/video0)\n");
	fprintf(stderr, "-p	dump current settings\n");
	fprintf(stderr, "-f x	set framerate (0...63)\n");
	fprintf(stderr, "-S w,h,x  set width/height/framerate\n");
	fprintf(stderr, "-b	store settings in nonvolatile RAM\n");
	fprintf(stderr, "-r	restore settings from nonvolatile RAM\n");
	fprintf(stderr, "-x	restore factory settings\n");
	fprintf(stderr, "-c x	set compression preference (0-3)\n");
	fprintf(stderr, "-g x	set automatic gain control (0...65535)\n");
	fprintf(stderr, "-s x	set shutter speed (1...65535)\n");
	fprintf(stderr, "-w auto/manual/indoor/outdoor/fl\n");
	fprintf(stderr, "	set automatic white balance mode\n");
	fprintf(stderr, "-a x	red-balance (only if -w manual) (0...65535)\n");
	fprintf(stderr, "-e x	blue-balance (idem) (0...65535)\n");
	fprintf(stderr, "-i x	speed of automatic white balance (1...65535)\n");
	fprintf(stderr, "-j x	delay for automatic white balance (1...65535)\n");
	fprintf(stderr, "-k x	set led on-time (0...25500ms\n");
	fprintf(stderr, "-l x	set led off-time\n");
	fprintf(stderr, "-m x	set electronic sharpness (0...65535)\n");
	fprintf(stderr, "-n x	set backlight compensation (0=0ff, other=on)\n");
	fprintf(stderr, "-o x	set antiflicker mode (0=0ff, other=on)\n");
	fprintf(stderr, "-q x	set noise reduction mode (0=none...3=high)\n");
	fprintf(stderr, "-t x	reset pan(bit 0) and/or tilt(bit 1)\n");
	fprintf(stderr, "-u	query pan/tilt status\n");
	fprintf(stderr, "-y x	set pan position\n");
	fprintf(stderr, "-z x	set tilt position\n");
	fprintf(stderr, "-h	this help\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "With this tool, you can only set settings specific to the Philips WebCams.\n");
}

int main(int argc, char *argv[])
{
	int fd = -1;
	int c;
	int loop;

	fprintf(stderr, "setpwc-astro v" VERSION "\n");

	for(loop=1; loop<argc; loop++)
	{
		if (strcmp(argv[loop], "-d") == 0)
		{
			device = argv[loop + 1];
			break;
		}
	}

        while((c = getopt(argc, argv, "S:d:hf:pbrxc:g:s:w:a:e:i:j:k:l:m:n:o:q:t:uv:y:z:")) != -1)
	{
		switch(c) {
		case 'S':
			{
				int w, h, f;
				char *c1 = strchr(optarg, ','), *c2 = NULL;
				if (c1) c2 = strchr(c1 + 1, ',');
				if (!c2) { fprintf(stderr, "-S got invalid parameter\n"); return 1; }
				w = atoi(optarg);
				h = atoi(c1+1);
				f = atoi(c2+1);
				check_device(&fd);
				set_dimensions_and_framerate(fd, w, h, f);
			}
			break;
		case 'd':
			device = optarg;
			break;
		case 'h':
			usage();
			return 0;
		case 'f':
			check_device(&fd);
			set_dimensions_and_framerate(fd, 0, 0, atoi(optarg));
			break;
		case 'p':
			check_device(&fd);
			dump_current_settings(fd);
			break;
		case 'b':
			check_device(&fd);
			flash_settings(fd);
			break;
		case 'r':
			check_device(&fd);
			restore_settings(fd);
			break;
		case 'x':
			check_device(&fd);
			restore_factory_settings(fd);
			break;
		case 'c':
			check_device(&fd);
			set_compression_preference(fd, atoi(optarg));
			break;
		case 'g':
			check_device(&fd);
			set_automatic_gain_control(fd, atoi(optarg));
			break;
		case 's':
			check_device(&fd);
			set_shutter_speed(fd, atoi(optarg));
			break;
		case 'w':
			check_device(&fd);
			set_automatic_white_balance_mode(fd, optarg);
			break;
		case 'a':
			check_device(&fd);
			set_automatic_white_balance_mode_red(fd, atoi(optarg));
			break;
		case 'e':
			check_device(&fd);
			set_automatic_white_balance_mode_blue(fd, atoi(optarg));
			break;
		case 'i':
			check_device(&fd);
			set_automatic_white_balance_speed(fd, atoi(optarg));
			break;
		case 'j':
			check_device(&fd);
			set_automatic_white_balance_delay(fd, atoi(optarg));
			break;
		case 'k':
			check_device(&fd);
			set_led_on_time(fd, atoi(optarg));
			break;
		case 'l':
			check_device(&fd);
			set_led_off_time(fd, atoi(optarg));
			break;
		case 'm':
			check_device(&fd);
			set_sharpness(fd, atoi(optarg));
			break;
		case 'n':
			check_device(&fd);
			set_backlight_compensation(fd, atoi(optarg));
			break;
		case 'o':
			check_device(&fd);
			set_antiflicker_mode(fd, atoi(optarg));
			break;
		case 'q':
			check_device(&fd);
			set_noise_reduction(fd, atoi(optarg));
			break;
		case 't':
			check_device(&fd);
			reset_pan_tilt(fd, atoi(optarg));
			break;
		case 'u':
			check_device(&fd);
			query_pan_tilt_status(fd);
			break;
		case 'y':
			check_device(&fd);
			set_pan_or_tilt(fd, SET_PAN, atoi(optarg));
			break;
		case 'z':
			check_device(&fd);
			set_pan_or_tilt(fd, SET_TILT, atoi(optarg));
			break;
		default:
			fprintf(stderr, "Internal error: unexpected option (%c %s).\n", c, optarg);
			break;
		}
	}

	if (fd != -1)
		close(fd);

	return 0;
}
